package com.noassertions.web;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItemIterator;
import org.apache.commons.fileupload.FileItemStream;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.iscreen.ValidationException;
import org.iscreen.ValidationServiceWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.noassertions.data.DAOInterface;
import com.noassertions.domain.Rider;
import com.noassertions.domain.User;
import com.noassertions.services.KdTreeService;
import com.noassertions.services.LuceneIndexer;

import edu.wlu.cs.levy.CG.KeyDuplicateException;
import edu.wlu.cs.levy.CG.KeySizeException;

@Controller
public class RiderController
{
    protected final Log log = LogFactory.getLog(getClass());
    private final DAOInterface dao;
	private ValidationServiceWrapper riderValidator;
    private final KdTreeService kDTreeService;
    private final LuceneIndexer luceneIndexer;
    private MessageSource messageSource;

    @Autowired
    public RiderController(DAOInterface dao, ValidationServiceWrapper riderValidator, KdTreeService kDTreeService, LuceneIndexer luceneIndexer, MessageSource messageSource) {
        this.dao = dao;
        this.riderValidator = riderValidator;
        this.kDTreeService = kDTreeService;
        this.luceneIndexer = luceneIndexer;
        this.messageSource = messageSource;
    }    
    
    /**
     * This method dumps the rider list in the view so that it can be displayed (most likely as JSON)
     * This is the standard GET for the Rider endpoint in a RESTful SOA 
     */    
	@RequestMapping(value = "/rider/{userid}", method = RequestMethod.GET)
	public String getRider(@PathVariable Integer userid, Model model)
	{
		ArrayList riders = dao.getRiderByUserId(userid);
		model.addAttribute("riders",riders);
				
		return "rider";
	}	
	
	/**
	 * Display form for new rider
	 */
	@RequestMapping(value = "/rider/new", method = RequestMethod.GET)
	public String newRiderDisplay(Model model)
	{
		//TODO: if user has already entered rider data, populate the form with that data
		UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		model.addAttribute("emailAddress", principal.getUsername());
		
		return "newrider";
	}
		
    /**
     * This method receives a new rider object from a spring form and will insert into DB if it is valid
     * This is the standard POST for the Rider endpoint in a RESTful SOA 
     * @throws IOException 
     * @throws FileUploadException 
     * @throws KeyDuplicateException 
     * @throws KeySizeException 
     */
	@RequestMapping(value = "/rider", method = RequestMethod.POST)
	public String createNewRider(HttpServletRequest request, Model model) throws FileUploadException, IOException, KeySizeException, KeyDuplicateException
	{

		// TODO: this form parsing should probably be factored out
		Rider rider = new Rider();
		ServletFileUpload upload = new ServletFileUpload();
		FileItemIterator iter = upload.getItemIterator(request);
		while (iter.hasNext()) {
			FileItemStream item = iter.next();
			String name = item.getFieldName();
			InputStream inputStream = item.openStream();
			// Get fields from form and add them to the bean
			if (name.equals("totalbodyheight")) {rider.setTotalbodyheight(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("totalbodyheight", rider.getTotalbodyheight());}
			if (name.equals("sternalnotch")) {rider.setSternalnotch(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("sternalnotch", rider.getSternalnotch());}
			if (name.equals("shoulderwidth")) {rider.setShoulderwidth(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("shoulderwidth", rider.getShoulderwidth());}
			if (name.equals("totalarm")) {rider.setTotalarm(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("totalarm", rider.getTotalarm());}
			if (name.equals("humerus")) {rider.setHumerus(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("humerus", rider.getHumerus());}
			if (name.equals("forearm")) {rider.setForearm(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("forearm", rider.getForearm());}
			if (name.equals("inseam")) {rider.setInseam(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("inseam", rider.getInseam());}
			if (name.equals("femur")) {rider.setFemur(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("femur", rider.getFemur());}
			if (name.equals("tibia")) {rider.setTibia(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("tibia", rider.getTibia());}
			if (name.equals("footlength")) {rider.setFootlength(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("footlength", rider.getFootlength());}
			if (name.equals("trunk")) {rider.setTrunk(Float.valueOf(Streams.asString(inputStream))); model.addAttribute("trunk", rider.getTrunk());}
			if (name.equals("notes")) {rider.setNotes(Streams.asString(inputStream)); model.addAttribute("notes", rider.getNotes());}
		}
		
		UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		model.addAttribute("emailAddress", principal.getUsername());
		User user = dao.getUserByemailAddress(principal.getUsername());
		log.debug("found user in DB: " + user.getEmailaddress());
		rider.setUser(user);
		
		// User submitted info is complete, so let's validate
		boolean valid = true;
		
		// This business of catching validation issues is not good, but it's the iscreen approach
		try {
			// TODO: right now, we only see that the date field is not null (since we check format above), we should have a better date validator
			// TODO: if the user submits a non-double value, what happens when we try to cast it as a double above?
			riderValidator.validate(rider);
		} catch (ValidationException e) {
			StringBuffer errors = new StringBuffer();
			ArrayList<String> failures = (ArrayList<String>) e.getFailureMessages();
			
			for (String failure : failures) {
				errors.append(failure); 
			}
			model.addAttribute("validationErrors", errors.toString());
			
			log.debug("rider submission did not validate: " + e.getFailureMessages());			
			valid = false;
		}

		if (valid) {
			dao.insertRider(rider);
			luceneIndexer.indexRider(rider);
			kDTreeService.addNewRider(rider);
			
			return "redirect:" + messageSource.getMessage("webAppUrl", null, Locale.getDefault()) + "rider/new/confirm";
		}
		else {
			// return with the errors we put in the model in our catch block above
			return "newrider";
		}
	}

	/**
	 * Display form for new user confirmation
	 */
	@RequestMapping(value = "/rider/new/confirm", method = RequestMethod.GET)
	public String newUser(Model model)
	{
		UserDetails principal = (UserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
		model.addAttribute("emailAddress", principal.getUsername());

		return "newriderconfirm";
	}
}